package com.mt.sdk.ble.base;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
import com.mt.sdk.ble.MTBLEManager;
import com.mt.sdk.ble.model.BLEBaseAction;
import com.mt.sdk.ble.model.ErroCode;
import com.mt.sdk.ble.model.WriteCharactWithAckAction;
import com.mt.sdk.ble.model.WriteDescriptorAction;
import com.mt.sdk.ble.model.BLEBaseAction.Option;
import com.mt.sdk.tools.MTTools;
import android.annotation.TargetApi;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.content.Context;
import android.os.Build;

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class TIOADBase extends BLEBase {

	public TIOADBase(Context context, MTBLEManager manager) {
		super(context, manager);
	}

	// 连接
	private boolean connectfail = false;
	private ErroCode connectfail_errocode;

	public ErroCode connect(String mac, int retryTimes, boolean isScan, CallBack callback) {
		if (callback == null) {
			return ErroCode.ERROMAP.get("null");
		}
		this.callback = callback;
		return super.connect(mac, retryTimes, isScan);
	}

	public ErroCode connect(String mac, int retryTimes, int rssi, CallBack callback) {
		if (callback == null) {
			return ErroCode.ERROMAP.get("null");
		}
		this.callback = callback;
		return super.connectWithRssi(mac, retryTimes, rssi);
	}

	// 查看是否有可用连接
	private boolean TIOADConnect = false;
	public boolean isTIOADConnect(){
		return TIOADConnect;
	}
	
	@Override
	protected void onBaseConnect(ErroCode errocode) {
		super.onBaseConnect(errocode);

		if (errocode.getCode() != 0) { // 连接失败的情况
			disConnect();
			callback.onConnect(errocode);
			return;
		}

		if (!getCharacts()) { // 获取特定服务失败的情况
			connectfail = true;
			connectfail_errocode = ErroCode.ERROMAP.get("getcharacterro");
			disConnect();
			return;
		}

		if (!enableNotify()) { // 使能可通知失败的情况
			connectfail = true;
			connectfail_errocode = ErroCode.ERROMAP.get("enablenotifyerro");
			disConnect();
			return;
		}
	}

	@Override
	protected void reTryConnect(int lasttimes) {
		super.reTryConnect(lasttimes);
		callback.reTryConnect(lasttimes);
	}

	@Override
	protected void onDisConnect(ErroCode errocode) {
		super.onDisConnect(errocode);
		TIOADConnect = false;
		
		if((files != null) && (files.isFinish()) && (callback != null)){
			super.clearCache();  // 清空缓存
			callback.onUpdataResult(ErroCode.ERROMAP.get("OK"));
		}
		callback.onDisConnect(errocode);
	}

	@Override
	protected void onManualDisConnect(ErroCode errocode) {
		super.onManualDisConnect(errocode);
		TIOADConnect = false;
		if (connectfail) {
			callback.onConnect(connectfail_errocode);
			return;
		}
		callback.onManualDisConnect(connectfail_errocode);
	}

	public static final String OAD_SERVICE_UUID = "f000ffc0-0451-4000-b000-000000000000";
	public static final String OAD_ImgIdentify_Charact = "f000ffc1-0451-4000-b000-000000000000";
	public static final String OAD_Block_Charact = "f000ffc2-0451-4000-b000-000000000000";
	private BluetoothGattCharacteristic mCharIdentify;
	private BluetoothGattCharacteristic mCharBlock;

	private boolean getCharacts() {
		BluetoothGattService data_service = getService(OAD_SERVICE_UUID);
		if (data_service == null) { // 服务获取失败的情况
			return false;
		}

		mCharIdentify = data_service.getCharacteristic(UUID.fromString(OAD_ImgIdentify_Charact));
		mCharBlock = data_service.getCharacteristic(UUID.fromString(OAD_Block_Charact));

		if ((mCharIdentify == null) || (mCharBlock == null)) {
			return false;
		}

		return true;
	}

	// 使能特征值的可通知属性
	private boolean enableNotify() {
		enableNotify(mCharIdentify, true);
		BluetoothGattDescriptor descriptor = mCharIdentify
				.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
		descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);

		addNewAction(new WriteDescriptorAction(descriptor, new BLEBaseAction.Option(2000)) {

			@Override
			public void onSuccess() {
				super.onSuccess();
			}

			@Override
			public void onFail(ErroCode erro) {
				super.onFail(erro);
				System.out.println("mBLEBase2.addNewAction1 onFail");
				connectfail = true;
				connectfail_errocode = ErroCode.ERROMAP.get("enablenotifyerro");
				disConnect();
			}

		});

		enableNotify(mCharBlock, true);
		descriptor = mCharBlock.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
		descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
		addNewAction(new WriteDescriptorAction(descriptor, new Option(2000)) {
			@Override
			public void onSuccess() {
				super.onSuccess();
				addTolisten();
				TIOADConnect = true;
				callback.onConnect(ErroCode.ERROMAP.get("OK"));
			}

			@Override
			public void onFail(ErroCode erro) {
				super.onFail(erro);
				connectfail = true;
				connectfail_errocode = ErroCode.ERROMAP.get("enablenotifyerro");
				disConnect();
			}
		});
		return true;
	}

	// 添加到监听列表
	private void addTolisten() {
		addListCharact(mCharIdentify);
		addListCharact(mCharBlock);
	}

	// 读取设备信息
	private int ver = 0;
	private Character deviceimgType = 'D';
	private int len = 0;
	public boolean readDeviceInf() {
		if(!isTIOADConnect()){
			return false;
		}
		addNewAction(new WriteCharactWithAckAction(mCharIdentify, mCharIdentify, new byte[] { 0, 0, 0, 0 }, new Option(1000)){

			@Override
			public void onReciveDatas(BluetoothGattCharacteristic characteristic, byte[] datas) {
				super.onReciveDatas(characteristic, datas);
				setStatues(ActionStatues.DONE);
				
				if(datas.length < 4){
					callback.onReadInf(0, 0, null, null, ErroCode.ERROMAP.get("unknow"));
					return;
				}
				
				ver = MTTools.convertUint16(datas[1], datas[0]);
				deviceimgType = ((ver & 1) == 1) ? 'B' : 'A';
				len = MTTools.convertUint16(datas[3], datas[2]);

				callback.onReadInf(ver/2, len, deviceimgType, null, ErroCode.ERROMAP.get("OK"));
			}

			@Override
			public void onFail(ErroCode erro) {
				super.onFail(erro);
			}
			
		});
		
		return true;
	}

	// 获取固件AB类型
	public Character getDeviceimgType(){
		return deviceimgType;
	}
	
	// 升级
	private OADFile files;
	public boolean upDatas(OADFile files){
		if(!isTIOADConnect()){
			return false;
		}
		this.files = files;
		
		addNewAction(new WriteCharactWithAckAction(mCharIdentify, mCharBlock, files.getUpdateHeader(), new Option(1000)){

			@Override
			public void onReciveDatas(BluetoothGattCharacteristic characteristic, byte[] datas) {
				super.onReciveDatas(characteristic, datas);
				setStatues(ActionStatues.DONE);

				sendBlock(datas);
			}

			@Override
			public void onFail(ErroCode erro) {
				super.onFail(erro);
			}
		});
		
		return true;
	}
	
	// 发送数据
	private boolean sendBlock(byte[] requestcode){
		if(!isTIOADConnect()){
			return false;
		}
		
		addNewAction(new WriteCharactWithAckAction(mCharBlock, mCharBlock, files.getOADBuffer(requestcode[0], requestcode[1]), new Option(1000)){

			@Override
			public void onReciveDatas(BluetoothGattCharacteristic characteristic, byte[] datas) {
				super.onReciveDatas(characteristic, datas);
				setStatues(ActionStatues.DONE);
				sendBlock(datas);
			}

			@Override
			public void onFail(ErroCode erro) {
				super.onFail(erro);
				
				if(!isConnected()){  // 如果失去了连接，就不需要做判断
					return;
				}
				
				if((files != null) && (files.isFinish())){  // 等待断开连接
					return;
				}
				
				callback.onUpdataResult(erro);
			}

		});
		
		callback.onUpdata(files.getUpdating_blocks());  // 升级进度
		
		return true;
	}
	
	// 回调
	private CallBack callback;

	public static interface CallBack {
		public void onConnect(ErroCode errocode);

		public void reTryConnect(int lasttimes);

		public void onDisConnect(ErroCode errocode);

		public void onManualDisConnect(ErroCode errocode);

		public void onUpdata(int process); // 进度

		public void onUpdataResult(ErroCode erro); // 升级失败

		public void onReadInf(int version, int len, Character imgType, byte[] uid, ErroCode erro); // 读取固件信息
	}

	// 文件对象
	public static class OADFile {
		private static final int OAD_IMG_HDR_SIZE = 8;
		private static final int FILE_BUFFER_SIZE = 0x40000;
		private static final int HAL_FLASH_WORD_SIZE = 4;
		private static final int OAD_BLOCK_SIZE = 16;
		private static final int OAD_BUFFER_SIZE = 2 + OAD_BLOCK_SIZE;
		private final byte[] mFileBuffer = new byte[FILE_BUFFER_SIZE];
		private byte[] file_uid = new byte[4];
		private int fileversion = 0;
		private int filelenght = 0;
		private int file_nBlocks = 0;
		private int updating_blocks = 0;
		private int updating_bytes = 0;
		private Character fileimgType;
		private boolean finish = false;

 		private OADFile() {
		}

		private OADFile(String filepath) {
			try {
				InputStream stream;
				File f = new File(filepath);
				stream = new FileInputStream(f);
				stream.read(mFileBuffer, 0, mFileBuffer.length);
				stream.close();
			} catch (IOException e) {
				right = false;
				return;
			}

			// Show image info
			fileversion = MTTools.convertUint16(mFileBuffer[5], mFileBuffer[4]);
			filelenght = MTTools.convertUint16(mFileBuffer[7], mFileBuffer[6]);
			fileimgType = (((fileversion & 1) == 1) ? 'B' : 'A');
			System.arraycopy(mFileBuffer, 8, file_uid, 0, 4);

			file_nBlocks = (int) (filelenght / (OAD_BLOCK_SIZE / HAL_FLASH_WORD_SIZE));
			System.out.println("file_nBlocks->" + file_nBlocks);

			updating_blocks = 0;
			updating_bytes = 0;

			right = true;
		}

		private boolean right = false;

		public boolean isRight() {
			return right;
		}

		public byte[] getFile_uid() {
			return file_uid;
		}

		public int getFileversion() {
			return fileversion/2;
		}

		public int getFilelenght() {
			return filelenght;
		}

		public int getFile_nBlocks() {
			return file_nBlocks;
		}

		public int getUpdating_blocks() {
			return updating_blocks;
		}

		public int getUpdating_bytes() {
			return updating_bytes;
		}

		public Character getFileimgType() {
			return fileimgType;
		}

		public byte[] getUpdateHeader() {
			byte[] buf = new byte[OAD_IMG_HDR_SIZE + 2 + 2];
			buf[0] = MTTools.converLoUint16(fileversion);
			buf[1] = MTTools.converHiUint16(fileversion);
			buf[2] = MTTools.converLoUint16(filelenght);
			buf[3] = MTTools.converHiUint16(filelenght);
			System.arraycopy(file_uid, 0, buf, 4, 4);
			return buf;
		}

		public byte[] getOADBuffer(byte index0, byte index1) {
			byte[] mOadBuffer = new byte[OAD_BUFFER_SIZE];
			updating_blocks = MTTools.convertUint16(index1, index0);
			updating_bytes = updating_blocks * OAD_BLOCK_SIZE;

			mOadBuffer[0] = index0;
			mOadBuffer[1] = index1;
			System.arraycopy(mFileBuffer, updating_bytes, mOadBuffer, 2, OAD_BLOCK_SIZE);
			
			updating_blocks++;
			if (updating_blocks >= file_nBlocks){
				finish = true;
			}else{
				finish = false;
			}

			return mOadBuffer;
		}

		public boolean isFinish() {
			return finish;
		}

		// 构建对象
		public static OADFile getFile(String path) {
			OADFile file = new OADFile(path);
			if (!file.isRight()) {
				return null;
			}
			return file;
		}
	}

}
